/*******************************************************************************
 * Copyright (c) 2014 Francesco Gabbrielli.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Francesco Gabbrielli - initial API and implementation
 ******************************************************************************/
package it.francescogabbrielli.android.solidalapp;

import java.util.Hashtable;
import java.util.Map;

import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import it.francescogabbrielli.android.solidalapp.SolidalApp.DataKey;
import it.francescogabbrielli.android.solidalapp.requestendpoint.model.Request;

/**
 * A {@link BaseFragment} which includes a {@link MapView}, implements some default 
 * marker logic and adds support for {@link Request}s
 *
 * @param <I> the listener interface to implement in the class that use this fragment
 */
public abstract class AbstractMapFragment<I> extends BaseFragment<I> 
			implements OnMapClickListener, OnMarkerClickListener, OnMarkerDragListener, OnInfoWindowClickListener {
	
	/** The map view */
	private MapView mapView;

	/** The map */
	protected GoogleMap map;
	
	/** If the marker info is shown */
	protected boolean mapShowInfo;
	
	/**
	 * Current location marker, set only on location change
	 * 
	 * @see DataKey#ADDRESS_LOCATION, DataKey#ADDRESS_LOCATION_MANUAL
	 */
	private Marker marker;
	
	/** Additional markers associated with requests */
	private Map<Marker, Request> requestMarkers;
	
	/**
	 * Get current position marker
	 * 
	 * @return
	 * 			the marker
	 */
	public Marker getMarker() {
		return marker;
	}
	
	/**
	 * Get the markers associated with the current requests
	 * 
	 * @return
	 * 			a map from markers to requests 
	 */
	public Map<Marker, Request> getRequestMarkers() {
		if (requestMarkers==null)
			requestMarkers = new Hashtable<Marker, Request>();
		return requestMarkers;
	}
	
	private void releaseMarker(Marker m) {
		MapUtils.stopBlinkIcon(m);
		m.remove();
	}
	
	/**
	 * Release the requests markers and remove them from map
	 */
	protected void releaseRequestMarkers() {
		if (requestMarkers!=null) {
			for (Marker m : requestMarkers.keySet())
				releaseMarker(m);
			requestMarkers.clear();
			requestMarkers = null;
		}
	}

	/**
	 * Release all the markers and remove them from the map
	 */
	protected void releaseMarkers() {
		mapShowInfo = false;
		if (marker!=null) {
			mapShowInfo = marker.isInfoWindowShown();
			releaseMarker(marker);
			marker = null;
		}
		releaseRequestMarkers();
	}	
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
		View view = inflater.inflate(R.layout.fragment_request_map, container, false);
		mapView = (MapView) view.findViewById(R.id.mapview);
		return view;
	}
	
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		
		super.onActivityCreated(savedInstanceState);
		
		if(mapView==null)
			mapView = (MapView) getView().findViewById(R.id.mapview);
		mapView.onCreate(savedInstanceState);
		mapShowInfo = savedInstanceState!=null && savedInstanceState.getBoolean("infoWindow");
		
//		initMap();
		
		try {
			MapsInitializer.initialize(getActivity());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putBoolean("infoWindow", mapShowInfo);
	}

	private void initMap() {
		map = mapView.getMap();
		map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
		map.setOnMapClickListener(this);
		map.setOnMarkerClickListener(this);
		map.setOnInfoWindowClickListener(this);
		map.setOnMarkerDragListener(this);
	}
		
	private void releaseMap() {
		releaseMarkers();
		map.setOnMapClickListener(null);
		map.setOnMarkerClickListener(null);
		map.setOnInfoWindowClickListener(null);
		map.setOnMarkerDragListener(null);
		map = null;
	}
	
	@Override
	public void onStart() {
		
		super.onStart();

		Log.i("MAP", "START");
		if(mapView==null)
			mapView = (MapView) getView().findViewById(R.id.mapview);
		
		initMap();
		
	}
	
	@Override
	public void onResume() {
		mapView.onResume();
		super.onResume();
	}

	@Override
	public void onPause() {
		mapView.onPause();
		super.onPause();
	}
	
	@Override
	public void onStop() {
		Log.i("MAP", "STOP");
		releaseMap();
		super.onStop();
	}
	
	@Override
	public void onDestroy() {
		if(mapView!=null)
			mapView.onDestroy();
		super.onDestroy();
	}
	
	@Override
	public void onStatusUpdate(StatusEvent event) {
		
	}

	@Override
	public void onDataChanged(DataEvent event) {
		
		DataKey key = event.getKey();

		switch (key) {
		
		// The address location is changed	
		case ADDRESS_LOCATION:
			Location location = (Location) app.getData(key);
			if(location!=null)
				centerPosition(new LatLng(location.getLatitude(), location.getLongitude()));
			//TODO: manage no location scenario
			break;
			
//		case ADDRESS_TEXT:
//			if (mapShowInfo && !event.isTriggered()) {
//				Marker m = getMarker();
//				if (m!=null) {
//					m.setTitle(" ");
//					m.showInfoWindow();
//				}
//			}
//			break;
			
		default:
			break;
		}
	}
	
	/**
	 * Center map camera on a marker, with animation
	 * 
	 * @param marker
	 * 				the marker
	 */
	protected void centerPosition(Marker marker) {
		CameraUpdate update = CameraUpdateFactory.newLatLng(marker.getPosition());
		map.animateCamera(update);
	}
	
	/**
	 * Center the current marker to a new position, or add a new marker
	 * if none present
	 * 
	 * @param pos
	 * 				the position at which to put the marker
	 */
	private void centerPosition(LatLng pos) {
		try {
			if (marker==null) {
				CameraUpdate update = CameraUpdateFactory.newLatLngZoom(pos, 16f);
				map.moveCamera(update);
				marker = map.addMarker(new MarkerOptions()
				        .position(pos)
				        .icon(BitmapDescriptorFactory.defaultMarker()));
			} else {
				marker.setPosition(pos);
				centerPosition(marker);
			}
		} catch(Exception e) {
			Log.e(SolidalApp.TAG, "Center position", e);
		}
	}
	

	@Override
	public void onMarkerDrag(Marker marker) {
		
	}

	@Override
	public void onMarkerDragEnd(Marker marker) {
		try {
			// build a manual location
			Location newLocation = new Location(app.getStatus().getLocation());
			newLocation.setLatitude(marker.getPosition().latitude);
			newLocation.setLongitude(marker.getPosition().longitude);
			app.setData(DataKey.ADDRESS_LOCATION_MANUAL, true);
			app.setData(DataKey.ADDRESS_TEXT, "");
			app.setData(DataKey.ADDRESS_LOCATION, newLocation);
		} catch(Exception e) {
			Log.e(SolidalApp.TAG, "Error dragging marker", e);
		}
	}

	@Override
	public void onMarkerDragStart(Marker marker) {
		
	}
	
	@Override
	public boolean onMarkerClick(Marker marker) {
		mapShowInfo = true;
//		centerPosition(marker);
//		marker.showInfoWindow();
//		return true;
		return false;
	}
		
	@Override
	public void onMapClick(LatLng pos) {
		marker.hideInfoWindow();
		mapShowInfo = false;
	}
	
}
